package com.ling.feign.plus.support;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.ling.feign.plus.FeignExceptionConstant;
import com.ling.feign.plus.exception.FeignServerBusinessException;
import com.ling.feign.plus.gson.IntegerTypeAdapter;
import com.ling.feign.plus.gson.StackTraceElementTypeAdapter;
import com.ling.feign.plus.gson.StringTypeAdapter;
import com.ling.feign.plus.model.BusinessExceptionModel;
import feign.Response;
import feign.RetryableException;
import feign.codec.ErrorDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;

/**
 * feign-client端
 * ERROR信息解码
 */
@Order(Ordered.HIGHEST_PRECEDENCE)
public class FeignServerExceptionDecoder extends ErrorDecoder.Default {

    private static final Logger log = LoggerFactory.getLogger(FeignServerExceptionDecoder.class);

    @Override
    public Exception decode(String methodKey, Response response) {
        Exception exception = super.decode(methodKey, response);

        // 如果是RetryableException，则返回继续重试
        if (exception instanceof RetryableException) {
            return exception;
        }
        Map<String, Collection<String>> headers = response.headers();

        if (response.status() == HttpServletResponse.SC_EXPECTATION_FAILED) {
            headers.get(FeignExceptionConstant.FEIGN_SERVER_BUSINESS_EXCEPTION);
            return decode0(methodKey, (LinkedList<String>) headers.get(FeignExceptionConstant.FEIGN_SERVER_BUSINESS_EXCEPTION));
        }
        // 其他异常进入fallback
        return feign.FeignException.errorStatus(methodKey, response);
    }

    private Exception decode0(String methodKey, LinkedList<String> exceptionJsons) {

        String exceptionJson = exceptionJsons.getFirst();
        try {
            exceptionJson = URLDecoder.decode(exceptionJson, "UTF-8");
        } catch (UnsupportedEncodingException ignored) {
        }
        log.info("methodKey: {}, decode exceptionJson: {}", methodKey, exceptionJson);
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(String.class, new StringTypeAdapter())
                .registerTypeAdapter(Integer.class, new IntegerTypeAdapter())
                .registerTypeAdapter(StackTraceElement.class, new StackTraceElementTypeAdapter())
                .create();
        BusinessExceptionModel exceptionModel = gson.fromJson(exceptionJson, BusinessExceptionModel.class);
        return new FeignServerBusinessException(exceptionModel.getMsg(), exceptionModel.getBusinessException());
    }

}
